// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "generator/internal/idempotency_policy_generator.h"
#include "google/cloud/internal/absl_str_cat_quiet.h"
#include "absl/memory/memory.h"
#include "generator/internal/codegen_utils.h"
#include "generator/internal/descriptor_utils.h"
#include "generator/internal/predicate_utils.h"
#include "generator/internal/printer.h"
#include <google/api/client.pb.h>
#include <google/protobuf/descriptor.h>

namespace google {
namespace cloud {
namespace generator_internal {

IdempotencyPolicyGenerator::IdempotencyPolicyGenerator(
    google::protobuf::ServiceDescriptor const* service_descriptor,
    VarsDictionary service_vars,
    std::map<std::string, VarsDictionary> service_method_vars,
    google::protobuf::compiler::GeneratorContext* context)
    : ServiceCodeGenerator("idempotency_policy_header_path",
                           "idempotency_policy_cc_path", service_descriptor,
                           std::move(service_vars),
                           std::move(service_method_vars), context) {}

Status IdempotencyPolicyGenerator::GenerateHeader() {
  HeaderPrint(CopyrightLicenseFileHeader());
  HeaderPrint(  // clang-format off
    "// Generated by the Codegen C++ plugin.\n"
    "// If you make any local changes, they will be lost.\n"
    "// source: $proto_file_name$\n"
    "\n"
    "#ifndef $header_include_guard$\n"
    "#define $header_include_guard$\n"
    "\n");
  // clang-format on

  // includes
  HeaderLocalIncludes({HasLongrunningMethod() ? "google/cloud/future.h" : "",
                       "google/cloud/internal/retry_policy.h",
                       "google/cloud/status_or.h", "google/cloud/version.h"});
  HeaderSystemIncludes({vars("proto_grpc_header_path"), "memory"});
  HeaderPrint("\n");

  auto result = HeaderOpenNamespaces();
  if (!result.ok()) return result;

  // Abstract interface ConnectionIdempotencyPolicy base class
  HeaderPrint(  // clang-format off
    "class $idempotency_class_name$ {\n"
    " public:\n"
    "  virtual ~$idempotency_class_name$() = 0;\n"
    "\n");
  // clang-format on

  HeaderPrint(  // clang-format off
    "  /// Create a new copy of this object.\n"
    "  virtual std::unique_ptr<$idempotency_class_name$> clone() const = 0;\n\n");
  // clang-format on

  for (auto const& method : methods()) {
    HeaderPrintMethod(
        method,
        {MethodPattern(
             {
                 // clang-format off
   {"  virtual google::cloud::internal::Idempotency\n"
    "  $method_name$($request_type$ const& request) = 0;\n"
        "\n",}
                 // clang-format on
             },
             All(IsNonStreaming, Not(IsPaginated))),
         MethodPattern(
             {
                 // clang-format off
   {"  virtual google::cloud::internal::Idempotency\n"
    "  $method_name$($request_type$ request) = 0;\n"
        "\n",}
                 // clang-format on
             },
             All(IsNonStreaming, IsPaginated))},
        __FILE__, __LINE__);
  }

  // close abstract interface Connection base class
  HeaderPrint(  // clang-format off
    "};\n\n");
  // clang-format on

  HeaderPrint(  // clang-format off
      "std::unique_ptr<$idempotency_class_name$>\n"
      "    MakeDefault$idempotency_class_name$();\n\n");
  // clang-format on

  HeaderCloseNamespaces();
  // close header guard
  HeaderPrint(  // clang-format off
      "#endif  // $header_include_guard$\n");
  // clang-format on
  return {};
}

Status IdempotencyPolicyGenerator::GenerateCc() {
  CcPrint(CopyrightLicenseFileHeader());
  CcPrint(  // clang-format off
    "// Generated by the Codegen C++ plugin.\n"
    "// If you make any local changes, they will be lost.\n"
    "// source: $proto_file_name$\n\n");
  // clang-format on

  // includes
  CcLocalIncludes(
      {vars("idempotency_policy_header_path"), "absl/memory/memory.h"});
  CcSystemIncludes({"memory"});
  CcPrint("\n");

  auto result = CcOpenNamespaces();
  if (!result.ok()) return result;

  CcPrint("using ::google::cloud::internal::Idempotency;\n\n");

  CcPrint(  // clang-format off
    "$idempotency_class_name$::~$idempotency_class_name$() = default;\n\n");
  // clang-format on

  // open anonymous namespace
  CcPrint("namespace {\n");

  CcPrint(  // clang_format off
      "class Default$idempotency_class_name$ : public "
      "$idempotency_class_name$ {\n"
      " public:\n"
      "  ~Default$idempotency_class_name$() override = default;\n\n"
      //  clang-format on
  );

  CcPrint(  // clang-format off
    "  /// Create a new copy of this object.\n"
    "  std::unique_ptr<$idempotency_class_name$> clone() const override {\n"
    "    return absl::make_unique<Default$idempotency_class_name$>(*this);\n"
    "  }\n\n");
  // clang-format on

  for (auto const& method : methods()) {
    CcPrintMethod(
        method,
        {MethodPattern(
             {
                 // clang-format off
   {"  Idempotency\n"
    "  $method_name$($request_type$ const&) override {\n"
    "    return Idempotency::$default_idempotency$;\n"
    "  }\n\n",}
                 // clang-format on
             },
             All(IsNonStreaming, Not(IsPaginated))),
         MethodPattern(
             {
                 // clang-format off
   {"  Idempotency\n"
    "  $method_name$($request_type$) override {\n"
    "    return Idempotency::$default_idempotency$;\n"
    "  }\n\n",}
                 // clang-format on
             },
             All(IsNonStreaming, IsPaginated))},
        __FILE__, __LINE__);
  }
  CcPrint(  // clang-format off
    "};\n"
    "}  // namespace\n\n");
  // clang-format on

  CcPrint(  // clang-format off
      "std::unique_ptr<$idempotency_class_name$>\n"
      "    MakeDefault$idempotency_class_name$() {\n"
      "  return absl::make_unique<Default$idempotency_class_name$>();\n"
      "}\n\n");
  // clang-format on

  CcCloseNamespaces();
  return {};
}

}  // namespace generator_internal
}  // namespace cloud
}  // namespace google
